package com.ruoyi.gis.task;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.gis.FileConfig;
import com.ruoyi.framework.gis.GeoserverConfig;
import com.ruoyi.framework.minio.MinioService;
import com.ruoyi.framework.task.AbstractTask;
import com.ruoyi.framework.task.TaskResult;
import com.ruoyi.gis.domain.DatasetRaster;
import com.ruoyi.gis.domain.GeoLayer;
import com.ruoyi.gis.domain.GisLayer;
import com.ruoyi.gis.geoserver.domain.GeoServerLayer;
import com.ruoyi.gis.geoserver.service.GeoserverService;
import com.ruoyi.gis.service.IDatasetRasterService;
import com.ruoyi.gis.service.IGisLayerService;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import org.geotools.coverage.Category;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.gce.image.WorldImageFormat;
import org.geotools.referencing.CRS;
import org.geotools.referencing.NamedIdentifier;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.util.NumberRange;
import org.geotools.util.factory.Hints;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;

import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class RasterDataPublishLayerTask extends AbstractTask {
    private final Logger logger = LoggerFactory.getLogger(RasterDataPublishLayerTask.class);
    private GeoserverConfig geoserverConfig;
    private IDatasetRasterService datasetRasterService;
    private IGisLayerService gisLayerService;
    private MinioService minioService;
    private GeoserverService geoserverService;
    private RuoYiConfig ruoYiConfig;
    private FileConfig fileConfig;
    Long dataRasterId;

    public RasterDataPublishLayerTask(Long dataRasterId) {
        this.dataRasterId = dataRasterId;
    }

    @Override
    public TaskResult call() {
        logger.info("publish raster tif layer......................");
        TaskResult result = new TaskResult();
        datasetRasterService = SpringUtils.getBean(IDatasetRasterService.class);
        ruoYiConfig = SpringUtils.getBean(RuoYiConfig.class);
        fileConfig = SpringUtils.getBean(FileConfig.class);
        geoserverConfig = SpringUtils.getBean(GeoserverConfig.class);
        gisLayerService = SpringUtils.getBean(IGisLayerService.class);
        minioService = SpringUtils.getBean(MinioService.class);
        geoserverService = SpringUtils.getBean(GeoserverService.class);
        DatasetRaster datasetRaster = datasetRasterService.selectDatasetRasterById(dataRasterId);
        if(datasetRaster==null){
            logger.info("publish is error task not exist......................");
            return result;
        }
        try {
            publishTif(datasetRaster);
        } catch (Exception e) {
            logger.info("db file info PublishLayerTask error......................");
            logger.error("PublishLayerTask",e);
        }
        return result;
    }
    private void publishTif(DatasetRaster datasetRaster) throws Exception {
        //本地是否有
        logger.info("publish raster tif layer start......................");
        File bucketFolder = new File(fileConfig.getDatasetRasterDir(),datasetRaster.getBucket());
        File rasterDateFolder = new File(bucketFolder,datasetRaster.getAcquisitionDate());
        File rasterFolder = new File(rasterDateFolder,datasetRaster.getDataId());
        File objectFile =  new File(rasterFolder,datasetRaster.getTifObjectName());
        if(!objectFile.exists()){
            logger.info("tif file info not exist......................");
            logger.info("db file info not exist in load dist will download from minio......................");
            //下载到本地
            ResponseEntity<byte[]> download = minioService.download(datasetRaster.getBucket(), datasetRaster.getTifObjectName());
            FileUtils.createParentDirectories(objectFile);
            FileUtils.writeByteArrayToFile(objectFile,download.getBody());
        }else {
            logger.info("publish raster tif use exist .......");
        }
        File geoserverRootFolder = new File(geoserverConfig.getGeoserverHostDataDirRoot(),"tif");
        File geoserverDockerRootFolder = new File(geoserverConfig.getGeoserverDataDir(),"tif");
        File tifFileFolder = new File(geoserverRootFolder,datasetRaster.getId()+"");
        File tifFileDockerFolder = new File(geoserverDockerRootFolder,datasetRaster.getId()+"");
        File tifFile = new File(tifFileFolder,datasetRaster.getId()+".tif");
        //geoserver 发布时的内部路径----geoserver的bug??
        File tifFileDocker = new File(tifFileDockerFolder,datasetRaster.getId()+".tif");
        if(tifFile.exists()){
            logger.info("db file in geoserver dir exist and delete it......................");
            tifFile.delete();
        }else {
            tifFileFolder.mkdirs();
        }
        tifFile.createNewFile();
        logger.info("db file info copy file to geoserver dir......................");
        FileUtils.copyFile(objectFile,tifFile);
        logger.info("db file info copy file to geoserver success......................");
        JSONObject tifInfo = new JSONObject();
        tifInfo.put("fileName",tifFile.getName());
        String layerName = datasetRaster.getId()+"";
        String layerTitle = "";
        String workspace = "raster";
        AbstractGridFormat format = GridFormatFinder.findFormat(tifFile);
        // this is a bit hacky but does make more geotiffs work
        Hints hints = new Hints();
        if (format instanceof GeoTiffFormat) {
            hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
        }
        AbstractGridCoverage2DReader reader = null;
        GridCoverage2D cov = null;
        if (format instanceof GeoTiffFormat) {
            hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
//            reader = format.getReader(tifFile, hints);
//            cov = reader.read(null);
        }else if(format instanceof WorldImageFormat){
//            reader = new WorldImageReader(tifFile,hints);
//            cov = reader.read(null);
        }
        reader = format.getReader(tifFile, hints);
        cov = reader.read(null);
        Envelope envelope = cov.getEnvelope();
        CoordinateReferenceSystem crs = cov.getCoordinateReferenceSystem();
        SingleCRS horizontalCRS = CRS.getHorizontalCRS(crs);
        if(horizontalCRS==null){
            horizontalCRS = CRS.getVerticalCRS(crs);
        }
        String crsStr = GeoServerRESTPublisher.DEFAULT_CRS;
        if(horizontalCRS!=null&&horizontalCRS.getIdentifiers()!=null
                &&horizontalCRS.getIdentifiers().size()>0){
            Object o = horizontalCRS.getIdentifiers().toArray()[0];
            crsStr = ((NamedIdentifier) o).toString();
        }
        tifInfo.put("crsStr",crsStr);
        tifInfo.put("crs",crs.toString());
        DirectPosition lowerCorner = envelope.getLowerCorner();
        double[] westSouth = lowerCorner.getCoordinate();
        DirectPosition upperCorner = envelope.getUpperCorner();
        double[] eastNorth = upperCorner.getCoordinate();
        GridGeometry2D gridGeometry = cov.getGridGeometry();
        GridEnvelope2D gridRange = gridGeometry.getGridRange2D();
        int height = gridRange.height;
        int width = gridRange.width;
        tifInfo.put("width",width);
        tifInfo.put("height",height);
        AffineTransform gridToCRS = (AffineTransform) gridGeometry.getGridToCRS();
        double xRes = XAffineTransform.getScaleX0(gridToCRS);
        double yRes = XAffineTransform.getScaleY0(gridToCRS);
        tifInfo.put("xRes",xRes);
        tifInfo.put("yRes",yRes);
        //起始点
        double translateX = gridToCRS.getTranslateX();
        double translateY = gridToCRS.getTranslateY();
        tifInfo.put("translateX",translateX);
        tifInfo.put("translateY",translateY);
//        envelopeParams.put(NORTH, eastNorth[1]);
//        envelopeParams.put(SOUTH, westSouth[1]);
//        envelopeParams.put(WEST, westSouth[0]);
//        envelopeParams.put(EAST, eastNorth[0]);
//        envelopeParams.put(XRES, xRes);
//        envelopeParams.put(YRES, yRes);
//        envelopeParams.put(ROWS, (double) height);
//        envelopeParams.put(COLS, (double) width);
        JSONObject bound = new JSONObject();
        bound.put("maxX",eastNorth[0]);//EAST 东
        bound.put("maxY",eastNorth[1]);//NORTH 北
        bound.put("minX",westSouth[0]);//WEST  西
        bound.put("minY",westSouth[1]);//SOUTH 南
        tifInfo.put("bbox",bound);
        int numBands = cov.getNumSampleDimensions();
        tifInfo.put("numBands",numBands);
        JSONArray bands = new JSONArray();
        for (int i = 0; i < numBands; i++) {
            JSONObject band = new JSONObject();
            GridSampleDimension dim = cov.getSampleDimension(i);
            double[] noDataValues = dim.getNoDataValues();
            double maximumValue = dim.getMaximumValue();
            double minimumValue = dim.getMinimumValue();
            ColorModel colorModel = dim.getColorModel();
            band.put("noDataValues",noDataValues!=null&&noDataValues.length>0?noDataValues[0]:null);
            band.put("maximumValue",maximumValue);
            band.put("minimumValue",minimumValue);
            if(colorModel!=null){
                int pixelSize = colorModel.getPixelSize();
                band.put("pixelSize",pixelSize);
            }
            band.put("description",dim.getDescription()!=null?dim.getDescription().toString():"");
//            band.put("");
            JSONArray cates = new JSONArray();
            List<Category> categories = dim.getCategories();
            if(categories!=null){
                for(Category category:categories) {
                    JSONObject cate = new JSONObject();
                    cate.put("name",category.getName()!=null?category.getName().toString():"");
                    NumberRange<? extends Number> range = category.getRange();
                    if(range!=null){
                        double maximum = range.getMaximum();
                        double minimum = range.getMinimum();
                        cate.put("maximum",maximum);
                        cate.put("minimum",minimum);
                    }
//                    Category.NODATA.getName().equals(category.getName())
                    cates.add(cate);
                }
            }
            band.put("categories",cates);
            //这里依次输出 RED_BAND  GREEN_BANK BLUE_BANK
            System.out.println(dim.getDescription().toString());
            bands.add(band);
        }
        tifInfo.put("bands",bands);
        reader.dispose();
        cov = null;
        reader = null;
        format = null;
        //如果shp、压缩包以及publishShp的参数layerName名称不一致，会发布失败 https://www.cnblogs.com/Jeely/p/14767996.html
        logger.info("publish geotif start crs............................................."+crs);
//        String destTifPath = tifFile.getCanonicalPath().
//                replace(geoserverConfig.getGeoserverHostDataDirRoot(),
//                geoserverConfig.getGeoserverDataDir());
//        tifFile = null;
//        File destFile = new File(destTifPath);
        tifFile = null;
        String storeName = datasetRaster.getId()+"";
        //publishExternalGeoTIFF 发布geoserver可以访问的已经存在的文件
//        boolean result = geoserverService.publishExternalGeoTIFF(
//                tifFileDocker,//tifFile,
//                workspace, storeName,
//                layerName, layerTitle, crsStr);
        //publishGeoTIFF 上传文件到geoserver进行发布（容易超时）
        boolean result = geoserverService.publishExternalGeoTIFF(
                tifFileDocker,//tifFile,
                workspace, storeName,
                layerName,layerName, crsStr);
        logger.info("publish geotif end workspace {} layerName {} layerTitle {} result {} ",
                workspace,layerName,layerTitle,result);
        if(!result){
            logger.error("publish tif layer error .......");
            return;
        }
        logger.info("save raster layer info ........");
        //获取图层信息
        GisLayer gisLayer = savePublishInfo(workspace,storeName, layerName, layerTitle, datasetRaster);
        if(gisLayer!=null){
            //保存gislayer 和文件关联
            datasetRaster.setLayerId(gisLayer.getLayerId());
            datasetRaster.setPublished(true);
//            if(tifInfo!=null){
//                markTask.setFileInfo(JSON.toJSONString(tifInfo));
//            }
//        if(shpInfo!=null)tblFile.setFileInfo(JSON.toJSONString(shpInfo));
            datasetRasterService.updateDatasetRaster(datasetRaster);
            datasetRasterService.updateFileLayerOfRaster(datasetRaster.getId(),gisLayer.getLayerId());
            logger.info("publish raster tif layer update used file layerId......................");
        }
        logger.info("publish raster tif layer end......................");
    }
    private GisLayer savePublishInfo(String workspace,String store,String layerName,
                                     String layerTitle,
                                     DatasetRaster datasetRaster) throws IOException {
        String wsAndLayer = workspace+":"+layerName;
        logger.info("publish layer ok savePublishInfo  will   getLayerConfig .......");
        GeoServerLayer layerConfig = geoserverService.getLayerConfig(wsAndLayer);
        if (layerConfig != null) {
            logger.info("publish layer  getLayer info .......");
            GeoLayer layer = geoserverService.getLayer(workspace,layerName);
            if (layer != null) {
                GisLayer query = gisLayerService.selectGisLayerByFileId(datasetRaster.getId());
                GisLayer subLayer = new GisLayer();
                if(query!=null){
                    subLayer.setLayerId(query.getLayerId());
                }else {
                    subLayer.setLayerId(IdUtil.getSnowflakeNextId());
                }
//                subLayer.setUserId(tblFile.getUserId());
                subLayer.setBzid(layerConfig.getId());
                subLayer.setSourceUrl(layer.getSourceUrl());
                subLayer.setLayerTitle(layerTitle);//(StringUtils.isEmpty(layer.getTitle()) ? layerTitle : layer.getTitle());
                subLayer.setLayerStyle(layer.getStyle());
                subLayer.setEndpoint(geoserverConfig.getEndpoint());
                subLayer.setLayerName(layer.getName());
                subLayer.setGroup(GisLayer.type_layer);//图层
                subLayer.setParentId(0L);
                subLayer.setAncestors("0,"+ subLayer.getLayerId() + ",");
                subLayer.setFileId(datasetRaster.getId());
                subLayer.setWorkspace(workspace);
                subLayer.setStoreType(layer.getStoreType());
                subLayer.setStore(store);///layer.getStoreName());//这个getStoreName 带有工作空间
                subLayer.setStatus("1");
                subLayer.setCrs(layer.getCrs());
                subLayer.setDelFlag("0");
                subLayer.setSourceType(layer.getType());
                subLayer.setLayerType(layer.getType());
                subLayer.setModel(GisLayer.model_file);
                String bounds = JSON.toJSONString(layer.getBoundary());
                subLayer.setBoundary(bounds);
                String[] mimeFormats = layerConfig.getMimeFormats();
                subLayer.setSourceFormat(mimeFormats != null ? mimeFormats[0] : null);
                if(query!=null){
                    gisLayerService.updateGisLayer(subLayer);
                }else {
                    gisLayerService.insertGisLayer(subLayer);
                }
                logger.info(" publish and save info ok  layer  {}  success  bounds {} .......",layer.getName(),bounds);
                return subLayer;
            }else {
                logger.info("publish layer  get layer info  null publish error .......");
            }
        }else {
            logger.info("publish layer  getLayerConfig null publish error .......");
        }
        return null;
    }
}
